#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
#endregion

namespace XnaGameComponents
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class ExtrudableQuad : Microsoft.Xna.Framework.DrawableGameComponent
    {
        private BoundingBox box;

        public BoundingBox BoundingBox
        {
            get { return box; }
            set { box = value; }
        }

        private Quad[] faces;

        public ExtrudableQuad(Game game)
            : base(game)
        {
            faces = new Quad[6];
        }

        private Vector3 upperLeft;

        public Vector3 UpperLeft
        {
            get { return upperLeft; }
            set { upperLeft = value; }
        }

        private Vector3 upperRight;

        public Vector3 UpperRight
        {
            get { return upperRight; }
            set { upperRight = value; }
        }

        private Vector3 lowerLeft;

        public Vector3 LowerLeft
        {
            get { return lowerLeft; }
            set { lowerLeft = value; }
        }

        private Vector3 lowerRight;

        public Vector3 LowerRight
        {
            get { return lowerRight; }
            set { lowerRight = value; }
        }

        private float height;

        public float Height
        {
            get { return height; }
            set { height = value; }
        }


        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            InitializeFaces();
            base.Initialize();
        }

        public virtual void InitializeFaces()
        {
            //top
            faces[0] = new Quad(
                new Vector3(upperLeft.X, upperLeft.Y + height, upperLeft.Z),
                new Vector3(upperRight.X, upperRight.Y + height, upperRight.Z),
                new Vector3(lowerLeft.X, lowerLeft.Y + height, lowerLeft.Z),
                new Vector3(lowerRight.X, lowerRight.Y + height, lowerRight.Z), 1);
            //bottom
            faces[1] = new Quad(lowerLeft, lowerRight, upperLeft, upperRight, -1);
            //front
            faces[2] = new Quad(faces[0].UpperRight, faces[0].UpperLeft, faces[1].LowerRight, faces[1].LowerLeft, -1);
            //right
            faces[3] = new Quad(faces[0].LowerRight, faces[0].UpperRight, faces[1].UpperRight, faces[1].LowerRight, -1);
            //back
            faces[4] = new Quad(faces[0].LowerLeft, faces[0].LowerRight, faces[1].UpperLeft, faces[1].UpperRight, -1);
            //left
            faces[5] = new Quad(faces[0].UpperLeft, faces[0].LowerLeft, faces[1].LowerLeft, faces[1].UpperLeft, -1);
            box = new BoundingBox(faces[1].LowerLeft, faces[0].LowerRight);
        }


        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here

            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            DrawQuad();
            Camera camera = (Camera)Game.Services.GetService(typeof(Camera));
            quadEffect.View = camera.View;
            quadEffect.Projection = camera.Projection;
            base.Draw(gameTime);
        }

        BasicEffect quadEffect;
        VertexDeclaration quadVertexDecl;
        protected override void LoadContent()
        {
            base.LoadContent();
            if (true)
            {
                // TODO: Load any ResourceManagementMode.Automatic content
                //Texture2D texture = new ContentManager(Game.Services).Load<Texture2D>("Textures/retro_backdrop");
                quadEffect = new BasicEffect(GraphicsDevice);
                quadEffect.EnableDefaultLighting();

                quadEffect.World = Matrix.Identity;
                /*quadEffect.View = View;
                quadEffect.Projection = Projection;*/
                quadEffect.TextureEnabled = true;
                //quadEffect.Texture = texture;
            }
            // TODO: Load any ResourceManagementMode.Manual content
            quadVertexDecl = VertexPositionNormalTexture.VertexDeclaration;
        }

        internal void LoadGraphicsContentInternal()
        {
            LoadContent();
        }



        private void DrawQuad()
        {
            Camera camera = (Camera)Game.Services.GetService(typeof(Camera));
            //quadEffect.Begin();
            quadEffect.View = camera.View;
            quadEffect.Projection = camera.Projection;
            foreach (EffectPass pass in quadEffect.CurrentTechnique.Passes)
            {
                pass.Apply();
                foreach (Quad face in faces)
                    GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(
                        PrimitiveType.TriangleList, face.Vertices, 0, 4, face.Indices, 0, 2);

                //pass.End();
            }
            //quadEffect.End();
        }

        int[] thirdPointIndex = new int[] { 1, 0, 3, 1, 2, 2 };
        public float? Intersects(Ray ray)
        {
            float? f = box.Intersects(ray);
            if (f != null)
                return f;
            else
            {
                Plane p1 = new Plane(lowerLeft, lowerRight, upperLeft);
                Plane p2 = new Plane(upperRight, upperLeft, lowerRight);

                if (p1.DotNormal(ray.Direction) == 0 && p2.DotNormal(ray.Direction) == 0)
                    return null;

                float? d1 = ray.Intersects(p1);
                float? d2 = ray.Intersects(p2);

                Vector3[] vectors = new Vector3[4];
                double[] angles = new double[4];

                Vector3 rayPosition1 = Vector3.Transform(ray.Position, Matrix.CreateShadow(-ray.Direction, p1));
                Vector3 rayPosition2 = Vector3.Transform(ray.Position, Matrix.CreateShadow(-ray.Direction, p2));
                if (ray.Position.Y + p1.D < 0 && ray.Position.Y + p2.D < 0)
                    return null;
                bool intersects = false;
                for (int j = 0; j < 2; j++)
                {
                    Vector3 a = faces[0].Vertices[faces[0].Indices[j * 3]].Position;
                    Vector3 b = faces[0].Vertices[faces[0].Indices[j * 3 + 1]].Position;
                    Vector3 c = faces[0].Vertices[faces[0].Indices[j * 3 + 2]].Position;
                    Vector3 p = (j == 0) ? rayPosition1 : rayPosition2;
                    //int direction = Vector3.Dot(x,((Vector3)(thirdPoint-v1)).Length());
                    if (SameSide(p, c, a, b) && SameSide(p, a, b, c) && SameSide(p, b, a, c))
                    {
                        intersects = true;
                        break;
                    }

                }
                if (intersects)
                    return d1;
                else
                    return null;

                /*
                if (anglemax - anglemin <= MathHelper.Pi)
                    return d1.Value;
                //s1 -= llangle + (lrangle - llangle) + (ulangle - (lrangle - llangle));
                //s2 -= urangle + (lrangle - urangle) + (ulangle - (lrangle - urangle));
                s1 %= MathHelper.TwoPi;
                s2 %= MathHelper.TwoPi;
                if (llangle + lrangle + ulangle == 0)
                    return d1.Value;
                if (urangle + lrangle + ulangle == 0)
                    return d2.Value;
                return null;

                /*
                        
                if (
                    Math.Max(Math.Max(ullength, urlength),Math.Max(lrlength,lllength)) - rlength >= 0 &&
                    Math.Abs(Math.Min(Math.Min(ullength, urlength), Math.Min(lrlength, lllength))) - rlength <= 0 &&
                    Math.Abs(Math.Max(Math.Max(lowerLeft.Z,lowerRight.Z), Math.Max(upperRight.Z,upperLeft.Z))) - Math.Abs(ray.Position.Z) >= 0 &&
                    Math.Abs(Math.Min(Math.Min(lowerLeft.Z, lowerRight.Z), Math.Min(upperRight.Z, upperLeft.Z))) - Math.Abs(ray.Position.Z) <= 0 &&
                    rayposition.Y>= 0)
                    return ray.Position.Y-p.D;
                return null;*/
            }
        }

        public bool SameSide(Vector3 p, Vector3 c, Vector3 a, Vector3 b)
        {
            return Vector3.Dot(Vector3.Cross(b - a, p - a), Vector3.Cross(b - a, c - a)) >= 0;
        }
    }
}


